<?php
use Workerman\Worker;
use Workerman\Timer;
use Workerman\Connection\TcpConnection;
use Workerman\Crontab\Crontab;

require_once 'vendor/autoload.php';
require_once 'config.php';

$redis = new Predis\Client($config['redis']);
// 设置时区
date_default_timezone_set('PRC');
// 不保存workman日志，防止低配服务器SSD爆满
Worker::$logFile = '/dev/null';
// nginx反向代理该2000端口
$ws_worker = new Worker("websocket://0.0.0.0:2000");
// 开启4个work
$ws_worker->count = 4;
// 实时在线人数
$connection_count = 0;

$ws_worker->onWorkerStart = function($connection) use ($redis)
{
    // 不存在则设置初始资金
    if (!$redis->hget('statistical', 'today_eq')) {
        $redis->hset('statistical', 'today_eq', $redis->hget('config', 'usdtInit'));
    }
    // 记录每日0点记录账户余额
    new Crontab('0 0 0 * * *', function() use ($redis) {
        echo '记录余额并清零今日做单次数'.PHP_EOL;
        // 记录日余额
        $redis->hset('statistical', 'today_eq', $redis->hget('statistical', 'eq'));
        // 日做单次数清零
        $redis->hset('statistical', 'today_count', 0);
    });
};

$ws_worker->onConnect = function($connection)
{
    // 有新的客户端连接时，在线人数+1
    global $connection_count;
    ++$connection_count;
};

$ws_worker->onClose = function($connection)
{
    // 客户端关闭时，连接数-1
    global $connection_count;
    $connection_count--;
};

$ws_worker->onMessage = function(TcpConnection $connection, $data) use ($redis)
{
	// 表单数据
	if ($data <> 'ping') {
		$data = json_decode($data, true);
		if (is_array($data)) {
			try {
                if (md5($data['password']) <> md5('131231')) throw new Exception('操作密码错误');

                if (empty($data['firstOrder'])) throw new Exception('首单数量不能为空');
                if (!is_numeric($data['firstOrder'])) throw new Exception('首单数量必须为整数');
                if ($data['firstOrder'] < 0) throw new Exception('首单数量不能小于0');

                if (!in_array($data['tactics'], [1, 2])) throw new Exception('补仓策略不正确');

                if (empty($data['usdtInit'])) throw new Exception('初始资金不能为空');
                if (!is_numeric($data['usdtInit'])) throw new Exception('初始资金必须为数字');
                if ($data['usdtInit'] < 0) throw new Exception('初始资金不能小于0');

                if (empty($data['allAddPositionNum'])) throw new Exception('最大补仓次数不能为空');
                if (!ctype_digit($data['profitRatio'])) throw new Exception('最大补仓次数必须为整数');
                if ($data['firstOrder'] < 0) throw new Exception('最大补仓次数不能小于0');
                if ($redis->hget('info', 'last')) {
                    $result = log10(($data['usdtInit'] / $redis->hget('info', 'last') * $redis->hget('info', 'lever')) / $data['firstOrder']) / log10(2);
                    $x = floor($result);
                    if ($data['allAddPositionNum'] > $x && ($data['tactics'] == 1)) throw new Exception('初始资金'.$data['usdtInit'].'，首单'.$data['firstOrder'].'，倍投时最大补仓最大为'.$x);
                }

                if (empty($data['profitRatio'])) throw new Exception('止盈率不能为空');
                if (!is_numeric($data['profitRatio'])) throw new Exception('止盈率必须为数字');
                if ($data['profitRatio'] < 0) throw new Exception('止盈率不能小于0');

                if (empty($data['profitCallbackRatio'])) throw new Exception('止盈回调不能为空');
                if (!is_numeric($data['profitCallbackRatio'])) throw new Exception('止盈回调必须为数字');
                if ($data['profitCallbackRatio'] < 0) throw new Exception('止盈回调不能小于0');
                if ($data['profitCallbackRatio'] > 100) throw new Exception('止盈回调不能大于100');

                if (empty($data['stopLossRatio'])) throw new Exception('补仓率不能为空');
                if (!is_numeric($data['stopLossRatio'])) throw new Exception('补仓率必须为数字');
                if ($data['stopLossRatio'] < 0) throw new Exception('补仓率不能小于0');

                if (empty($data['addPositionCallbackRatio'])) throw new Exception('补仓回调不能为空');
                if (!is_numeric($data['addPositionCallbackRatio'])) throw new Exception('补仓回调必须为数字');
                if ($data['addPositionCallbackRatio'] < 0) throw new Exception('补仓回调不能小于0');
                if ($data['addPositionCallbackRatio'] > 100) throw new Exception('补仓回调不能大于100');

                if (!in_array($data['status'], [false, true])) throw new Exception('机器人状态不正确');
                if ($data['status'] == false) {
                    $redis->set('stop_robot', 1);
                } else {
                    $redis->del('stop_robot');
                    // 上10秒开单锁，防止真空期间多空双开
                    $redis->set('orderLock', 1, 'ex', 10, 'nx');
                    // 清除redis相关key
                    $redis->del('config');
                    $redis->del('direction');
                    $redis->del('info');
                    $redis->del('lastNum');
                    $redis->del('positionLock');
                    $redis->del('statistical');
                }

                $config = <<<EOF
<?php

\$config = [
    'redis'=>[
        'host'     => '127.0.0.1',
        'port'     => 6379,
        'database' => 0,
        'password' => 'a123654!@#',
    ],
    'keysecret' =>  [
        'key'       =>  '3ef36f20-8415-474c-903a-89c6bb6146ca',
        'secret'    =>  '8CC1B8AB14D551A8FCB43EEE955CA37A',
        'passphrase'=>  'a123654',
    ],
    'other' =>  [
        // 止盈率
        'profitRatio'               =>  ${data['profitRatio']},
        // 补仓/止损率
        'stopLossRatio'             =>  ${data['stopLossRatio']},
        // 止盈回调率
        'profitCallbackRatio'       =>  ${data['profitCallbackRatio']},
        // 补仓回调率
        'addPositionCallbackRatio'  =>  ${data['addPositionCallbackRatio']},
        // 补仓策略，1倍投，2等额，默认倍投
        'tactics'                   =>  ${data['tactics']},
        // 补仓次数
        'allAddPositionNum'         =>  ${data['allAddPositionNum']},
        // 首单数量
        'firstOrder'                =>  ${data['firstOrder']},
        // USDT初始资金
        'usdtInit'                  =>  ${data['usdtInit']},
		// 永续交易对
		'currency'					=>	"LRC-USDT-SWAP",
		// 二级标题
        'subTitle'                  =>  "${data['subTitle']}",
    ],
];
EOF;
                $result = file_put_contents('config.php', $config, LOCK_EX);
                if ($result === false) throw new Exception('写入配置失败');
                $result2 = system("
                    sudo /www/server/panel/pyenv/bin/supervisorctl restart account:account_00 && 
                    cd /www/wwwroot/robot && 
                    git add . && 
                    git commit -m '更新' && 
                    git push"
                );
                if ($result2 === false) throw new Exception('重启服务失败');
                $msg = '操作成功';
            } catch (Exception $e) {
                $msg = $e->getMessage();
            }
            // 组装数据
            $res = json_encode([
                'code'	=>	1,
                'msg'	=>	$msg,
            ], JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES);
            // 结果推送给客户端
            $connection->send($res);
		}
	}
	
	// 定时器，每2秒推送一次
    Timer::add(2, function() use ($connection, $redis) {
        // 在线人数
        global $connection_count;
        // 构建数据
        $res = json_encode([
            'code'  =>  0,
            'msg'   =>  '',
            'data'  =>  [
                // 运行日志
                'list'          				=>  $redis->lrange('robotLog', 0, 12),
                // 其他数据
                'other'         				=>  [
                    // USDT初始资金
                    'usdtInit'             	    =>  ($redis->hget('config', 'usdtInit') ?? 0).' USDT',
                    // 止盈率
                    'profitRatio'           	=>  ($redis->hget('config', 'profitRatio') ?? 0).' %',
                    // 止损率
                    'stopLossRatio'         	=>  ($redis->hget('config', 'stopLossRatio') ?? 0).' %',
                    // 最大补仓
                    'allAddPositionNum'     	=>  ($redis->hget('config', 'allAddPositionNum') ?? 0).' 次',
                    // 首单数量
                    'firstOrder'            	=>  $redis->hget('config', 'firstOrder').' '.$redis->hget('config', 'currencyCoin'),
					// 交易对
					'currency'					=>	$redis->hget('config', 'currency'),
					// 交易币种
					'currencyCoin'				=>	$redis->hget('config', 'currencyCoin'),
                    // 账户总权益
                    'eq'                    	=>  ($redis->hget('statistical', 'eq') ?? 0).' USDT',
                    // 可用保证金
                    'availEq'               	=>  ($redis->hget('statistical', 'availEq') ?? 0).' USDT',
                    // 占用保证金
                    'frozenBal'             	=>  ($redis->hget('statistical', 'frozenBal') ?? 0).' USDT',
                    // 保证金率
                    'mgnRatio'              	=>  ($redis->hget('statistical', 'mgnRatio') ?? 0).' %',
                    // 实际杠杆
                    'notionalLever'         	=>  $redis->hget('statistical', 'notionalLever').' x',
                    // 未实现盈亏
                    'upl'                   	=>  ($redis->hget('info', 'upl') ?? 0).' USDT',
                    // 总盈利
                    'total_profit'          	=>  ($redis->hget('statistical', 'total_profit') ?? 0).' USDT',
                    // 总盈利率
                    'total_profit_ratio'    	=>  ($redis->hget('statistical', 'total_profit_ratio') ?? 0).' %',
                    // 今日盈利
                    'today_profit'          	=>  ($redis->hget('statistical', 'today_profit') ?? 0).' USDT',
                    // 今日盈利率
                    'today_profit_ratio'    	=>  ($redis->hget('statistical', 'today_profit_ratio') ?? 0).' %',
					// 今日做单
					'today_count'           	=>  ($redis->hget('statistical', 'today_count') ?? 0).' 笔',
                    // 开仓平均价
                    'avgPx'                 	=>  $redis->hget('info', 'avgPx'),
                    // 最新成交价
                    'last'                  	=>  $redis->hget('info', 'last'),
                    // 本单收益率
                    'uplRatio'              	=>  ($redis->hget('info', 'uplRatio') ?? 0).' %',
                    // 杠杆倍数
                    'lever'                 	=>  $redis->hget('info', 'lever').' x',
					// 本单已补仓
					'addPositionNum'        	=>  ($redis->get('addPositionNum') ?? 0).' 次',
                    // 在线人数
                    'online'                	=>  $connection_count.' 人',
					// 爆仓价
					'liqPx'			            =>	$redis->hget('info', 'liqPx'),
                    // 补仓回调率
                    'addPositionCallbackRatio'  =>  ($redis->hget('config', 'addPositionCallbackRatio') ?? 0).' %',
                    // 止盈回调率
                    'profitCallbackRatio'       =>  ($redis->hget('config', 'profitCallbackRatio') ?? 0).' %',
					// 小标题
                    'subTitle'       			=>  $redis->hget('config', 'subTitle'),
					// 开单方向
					'direction'					=>	$redis->get('direction') ?? "无",
					// 当前回调率
					'downRate'					=>	$redis->get('downRate') ? $redis->get('downRate').' %' : '无',
					// 反弹前收益率
					'upBeforeRatio'				=>	$redis->get('upBeforeRatio') ? $redis->get('upBeforeRatio').' %' : '无',
					// 回调前收益率
					'downBeforeRatio'			=>	$redis->get('downBeforeRatio') ? $redis->get('downBeforeRatio').' %' : '无',
                    // 补仓策略
                    'tactics'                   =>  $redis->hget('config', 'tactics'),
					// 量化开关
					'status'					=>	$redis->get('stop_robot') ? true : false,
                ],
            ],
        ], JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES);
        
        //推送给客户端
        $connection->send($res);
    });
};

Worker::runAll();